From bc0fe8ff74c3e462c9d637eb3e93645f0b97a3f8 Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Tue, 31 Mar 2009 11:54:12 +0100 Subject: [PATCH] vtd: fix multiple Dom0 S3 on hosts that support Queued Invalidation. On such hosts we can't do multiple Dom0 S3 when VT-d is enabled. The cause is: during the first S3 resume, init_vtd_hw() initializes the invalidation function pointers to the register-based ones and later enable_qinval() forgets to overwrite the flush function pointers to queued-based ones, so actually Queued Invalidaton is enabled, but we actually use the register-based invalidation function! Later during the second Dom0 S3, in iommu_suspend() -> iommu_flush_all(), we try to use the register-based invalidation functions to perform global flush while Queued Invalidation is enabled, and this can cause a host reset because VT-d spec says: when the queued invalidation is enabled, software must submit invalidation commands only through the IQ (and not through any invalidation command registers). The attached patch fixes the buggy enable_qinval(). And in iommu_resume(), we invoke iommu_flush_all() for safety. Signed-off-by: Dexuan Cui --- xen/drivers/passthrough/vtd/iommu.c | 20 +++++++++++++++++++- xen/drivers/passthrough/vtd/qinval.c | 5 +++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c index e2b026ca7e..dc18b3fde5 100644 --- a/xen/drivers/passthrough/vtd/iommu.c +++ b/xen/drivers/passthrough/vtd/iommu.c @@ -1953,16 +1953,34 @@ void iommu_resume(void) { struct acpi_drhd_unit *drhd; struct iommu *iommu; + struct iommu_flush *flush; u32 i; if ( !vtd_enabled ) return; + /* Re-initialize the register-based flush functions. + * In iommu_flush_all(), we invoke iommu_flush_{context,iotlb}_global(), + * but at this point, on hosts that support QI(Queued Invalidation), QI + * hasn't been re-enabed yet, so for now let's use the register-based + * invalidation method before invoking init_vtd_hw(). + */ + if ( iommu_qinval ) + { + for_each_drhd_unit ( drhd ) + { + iommu = drhd->iommu; + flush = iommu_get_flush(iommu); + flush->context = flush_context_reg; + flush->iotlb = flush_iotlb_reg; + } + } + /* Not sure whether the flush operation is required to meet iommu * specification. Note that BIOS also executes in S3 resume and iommu may * be touched again, so let us do the flush operation for safety. */ - flush_all_cache(); + iommu_flush_all(); if ( init_vtd_hw() != 0 && force_iommu ) panic("IOMMU setup failed, crash Xen for security purpose!\n"); diff --git a/xen/drivers/passthrough/vtd/qinval.c b/xen/drivers/passthrough/vtd/qinval.c index e82337045f..60c7e0ad70 100644 --- a/xen/drivers/passthrough/vtd/qinval.c +++ b/xen/drivers/passthrough/vtd/qinval.c @@ -432,10 +432,11 @@ int enable_qinval(struct iommu *iommu) "Cannot allocate memory for qi_ctrl->qinval_maddr\n"); return -ENOMEM; } - flush->context = flush_context_qi; - flush->iotlb = flush_iotlb_qi; } + flush->context = flush_context_qi; + flush->iotlb = flush_iotlb_qi; + /* Setup Invalidation Queue Address(IQA) register with the * address of the page we just allocated. QS field at * bits[2:0] to indicate size of queue is one 4KB page. -- 2.30.2